unit uDZIOCP;

interface

uses
  Windows, uDZWinsock2, uDZMSWSock, uDZSocketUtils, uDZThread;

const
  MIN_ACCEPTEX_BUFFER = 2*(SizeOf(TDzSockAddrUnion) + 16);

type
  TDzIocpIOService = class;
  PDzAsyncOperation = ^TDzAsyncOperation;

  TDzAsyncCallback = procedure(IOService: TDzIocpIOService;
    CompletionKey: TObject; Operation: PDzAsyncOperation) of object;

  TDzAsyncOperation = record
    Overlapped: TOverlapped;
    CallBack: TDzAsyncCallback;
  end;

  TDzIocpIOService = class
  private
    fHandle: THandle;
  protected
    function GetQueuedCompletionStatus: Boolean;  
  public
    constructor Create;
    destructor Destroy; override;
    procedure Bind(hFile: THandle; Key: TObject);
    procedure Run;
    procedure SendQuitSignal(Count: Integer = 1);
    property Handle: THandle read fHandle;
  end;

procedure ResetAsyncOperation(operation: PDzAsyncOperation;
  callback: TDzAsyncCallback);

implementation

type
  TIocpWorkThread = class(TDzThread)
  protected
    procedure Execute; override;
  end;

procedure ResetAsyncOperation(operation: PDzAsyncOperation;
  callback: TDzAsyncCallback);
begin
  operation.Overlapped.Internal := 0;
  operation.Overlapped.InternalHigh := 0;
  operation.Overlapped.Offset := 0;
  operation.Overlapped.OffsetHigh := 0;
  operation.Overlapped.hEvent := 0;
  operation.CallBack := callback;
end;

{ TDzIocpIOService }

procedure TDzIocpIOService.Bind(hFile: THandle; Key: TObject);
begin
  CreateIoCompletionPort(hFile, Self.Handle, DWORD(Key), 0);
end;

constructor TDzIocpIOService.Create;
begin
  fHandle := CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 0);
end;

destructor TDzIocpIOService.Destroy;
begin
  CloseHandle(fHandle);
  fHandle := 0;
  inherited;
end;

function TDzIocpIOService.GetQueuedCompletionStatus: Boolean;
var
  Operation: PDzAsyncOperation;
  IObj: TObject;
  BytesTransferred: DWORD;
begin
  Result := False;
  try
    if Windows.GetQueuedCompletionStatus(Self.Handle, BytesTransferred,
      DWORD(IObj), POverlapped(Operation), INFINITE) then
    begin
      if (BytesTransferred = 0) and (IObj = nil) then
      begin
        //signal set with PostQueuedCompletionStatus
        //FmtWirteLog('WorkThread (id %u) will quit'#13#10), [ThreadId]);
        Exit;
      end;

      if Assigned(Operation.Callback) then
      try
        Operation.CallBack(Self, IObj, Operation);
      except
        //handle exception
      end;
      Result := True;
    end
    else begin
      //wait time out
      if Operation = nil then Exit;

      //error
      try
        Operation^.CallBack(Self, IObj, Operation);
      except

      end;
      Result := True;
    end;
  except
    
  end;
end;

procedure TDzIocpIOService.Run;
begin
  while Self.GetQueuedCompletionStatus do;
end;

procedure TDzIocpIOService.SendQuitSignal(Count: Integer);
var
  i: Integer;
begin
  for i := 0 to Count - 1 do
    Windows.PostQueuedCompletionStatus(Handle, 0, 0, nil);
end;

{ TIocpWorkThread }

procedure TIocpWorkThread.Execute;
var
  Iocp: TDzIocpIOService;
begin
  Iocp := TDzIocpIOService(Self.ThreadParameter);
  Assert(Iocp <> nil, 'TDzIocpIOService object not found!');
  Iocp.Run;
end;

end.
